Skip to content

Sync ako/mxcli: Java action template and enum parameters, access rules after drop entity, splice refusals, STRING_AGG, mxcli test false greens - #1649

Merged
ako merged 18 commits into
mendixlabs:mainfrom
ako:main
Oct 25, 2026

Conversation

@ako

@ako ako commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into upstream: 13 commits from fork PRs ako#1687, ako#1688, ako#1689, ako#1691 and ako#1692.

Fixes #1551, #1554, #1604 and #1641. Refs #1517.

Java and JavaScript actions (#1554)

  • call java action writes a text-template or document parameter as an expression (call java action writes a text-template or document parameter as a plain expression (CE0115) #1554).
    • A StringTemplate(Text|Sql) parameter, such as BusinessEvents.PublishBusinessEvent_V2's Subject, or a custom-document parameter was bound as the argument's source text.
    • That gave CE0115.
  • create java action writes a StringTemplate parameter as a template.
    • It used to be written as a String, which is the form describe prints for a text-template parameter.
    • So a describe → exec of PublishBusinessEvent_V2 turned its template into a String, and broke every call Studio Pro had written.
  • A bare enumeration type in a Java or JavaScript action signature is an enumeration. It was stored as an entity, and mxbuild reported CE1613.
  • New refusals:
    • create javascript action with a StringTemplate type is refused (MDL-JSA01). That is a Java-only type, and it was silently written as a String.
    • An unreturnable return type is refused (MDL-JA03). returns StringTemplate, Binary, AutoNumber and entity <T> were all written as a Boolean action.

Domain model access rules (#1551)

Microflows and views (#1517, #1641)

mxcli test false greens (#1604)

Still open in #1517

These need a maintainer decision:

  1. A structural change inside a loop body is still refused. The options are an explicit replace body mode, or replacing the whole loop.
  2. Re-running a script after another script spliced a decision into the flow would need dropping that decision, which is refused.
  3. @annotation(id: n1) in an alter fragment can't reliably refer to a stored note. The recommendation is to address a note by its text.

Validation

  • go test ./..., go vet and gofmt are clean.
  • TestMxCheck_DoctypeScripts gives 0 errors on 11.13.0 for the microflow, nanoflow, error-handling, REST client and workflow scripts.
  • Describe → exec was run on 29 PedApp flows and 260 TestApp flows.
  • Each fix has a test, a bug-test script and a finding

ako and others added 18 commits October 25, 2026 10:25
…used (#856)

`find $L where Code = 'x'` is stored as `$currentObject/Code = 'x'`: the
builder qualifies a bare member of a find/filter-by-expression predicate
(qualifyIteratorAttributes, #1002), and describe prints the stored form.
create or modify matches the declared statements against that
description, so the statement as the script spells it never matched its
own stored activity. An unchanged re-run still read as Unchanged (the
built flow is the stored one), but a re-run with one real change sent the
flow to the statement diff, where the find read as changed too: needlessly
replaced away from the change, and next to it merged with the change into
one replace whose fragment declares $A while the stored find still does —
refused under mdl 1 ("the fragment declares $A, which the microflow
already has"), the whole flow rebuilt under mdl 0.

This is the "other clauses with two parse paths" item of the issue.
describedMemberSpellings now rewrites a find/filter-by-expression
predicate with the builder's own qualifyIteratorAttributes before the
statement diff, keeping it as written where the builder would refuse it.

Found by probing: each flow created, then re-run with one inserted
statement, flagging any summary but "1 inserted". Over the 118 flows of
02-microflow-examples and 23 hand-picked respellings (sort without asc,
qualified members, association spellings, `<>`, aggregates, enumeration
values, …) this clause was the only mismatch left.

The issue's other two items need no change: `diff` runs a script's
earlier statements since 4c1e5c3 (#907: it runs exec on a scratch copy);
verified on a blank 11.13 app — a script creating SUB_New and calling it
from a create or modify of a stored flow diffs as "1 new, 1 modified".
The TestApp parity test's CI time is a scheduling note, not a defect.

Verification:
- TestCreateOrModify_ListOperationWhereBareMemberIsItsStoredActivity
  (mdl 1 and mdl 0): before the fix refused / rebuilt; after, "spliced:
  1 replaced" with the find and filter keeping their $IDs and places.
  Control TestCreateOrModify_ListOperationWhereChangedPredicateIsAChange:
  a changed predicate, in either spelling, is still replaced.
- Revert proof: with flow_member_spelling.go restored both tests fail
  with the reported refusal.
- Blank 11.13.0 app, mdl-examples/bug-tests/splice-856-…mdl: baseline
  refuses statement 4 and rolls back; fixed prints "Modified microflow:
  BugTest856.F (spliced: 1 replaced)"; mx check 0 errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…access rule (#1449)

`drop entity` removes every association with the entity at either end — the
backend cascades into other modules' domain models too — and reconciled no
access rule, so the member entry for each removed association stayed on the
entity at the other end: mx check CE1613 "The selected association
'Shop.Order_Customer' no longer exists" (upstream mendixlabs#1551).

Cause, in two layers:
- execDropEntity (mdl/executor/cmd_entities.go) never reconciled, and
  execDropAssociation reconciled only the declaring module, though under
  OWNER Both a cross-module association is a member of its TO entity in
  another module.
- ReconcileMemberAccesses (mdl/backend/modelsdk/domainmodel_security_write.go)
  preserved every association reference qualified by another module — the
  rule written for associations inherited across modules (#758) — so even a
  reconcile of the right module could not remove an entry for an association
  that no longer exists. `update security` reported "All entity access rules
  are up to date" over it.

Fix: drop entity collects, before the delete, every module that can hold a
member entry for one of the entity's associations (its own, each cross-module
TO end's, each module declaring a cross-module association to it) and
reconciles them through reconcileModulesAccess — the helper alter association
already used, now also behind drop association, which passes the TO end's
module as well. The reconcile now removes a foreign reference whose declaring
module exists and no longer declares the association; System (virtual domain
model), absent modules and existing associations stay preserved.

Verified on a blank 11.13.0 app with security on, mx check before (baseline
binary) / after: drop of a same-module TO end 1 -> 0 errors, of a cross-module
TO end 1 -> 0, of the FROM end of a cross-module OWNER Both association 1 -> 0,
of a FROM end 1 -> 0, `drop association` of a cross-module OWNER Both
association 1 -> 0, the TO end being Administration.Account (a System.User
specialization) 3 -> 0; `update security` repairs a project broken by the
baseline binary (1 -> 0). An association inherited across modules
(Shop.Special extends Crm.Lead) keeps its entry. Tests revert-proven: with the
fix reverted, the backend and executor tests fail with the reported symptoms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirror of mendixlabs#1604.

Cause: parseAnnotations (cmd/mxcli/testrunner/parser.go) read the doc
comment line by line, and the @expect/@verify patterns captured only the
rest of the tag's own line. A continuation line carries no tag, so it
matched nothing and was dropped without a message. When the first line
was a complete expression the test asserted something else: with `and`
it passed on less, with `or` it failed although the whole assertion
held. A bare `@expect` with the expression below it was dropped whole,
because the pattern required text on the tag line.

Fix: @expect and @verify open an annotation whose text runs, as a
javadoc block tag's does, to the next @tag or the end of the comment;
the joined text is what ParseExpect/ParseVerify compile. Text after
the tag that does not continue the expression is therefore an ERROR
(MDL-TEST01 in check) whose message says the annotation spans N lines
and that a description goes before the first tag; it is never skipped.
@test/@setup/@cleanup/@throws stay one line.

Case B: `check -p` on a lone test file reported "microflow not found"
for a target a setup script creates. The script-set form already
resolves it; the not-found report now names it, and check/test --help
say a test file can be one of the files.

Verification: parser_multiline_annotation_test.go and
TestCheck_TestFileMissingTargetPointsAtTheScriptSet fail with the fix
reverted (Raw = "$r = 'zzz'", no hint). Runtime A/B on 11.13.0 with
`mxcli test --local --db-type hsqldb`: the baseline PASSED
`$r = 'a;b'` + `and length($r) = 4` and FAILED the true `or` assertion
("expected $r = 'zzz', actual: a;b"); the fix inverts both. The
bug-test fixtures discriminate in `make check-mdl` (baseline fails the
positive one and passes the .fail one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cause: pageProbeJS (cmd/mxcli/docker/pagecheck.go) counted a page as
settled once its body text held still for three 250ms polls with no
progress element, capped at 4s, and left the cap silently. Measured on
11.13.0 with a list view whose microflow data source takes 2s and 8s:
while the data is out, .mx-listview sits empty with no progress
element, no aria-busy and no "no items" placeholder, and the text holds
still. The only sign is the open POST /xas/ runtimeOperation. So both
pages were measured empty ("text=37", no rows= field) and
`playwright check` exited 0. A data grid 2 does show an aria-busy
spinner, which is why grids were measured correctly.

Fix: the probe tracks the page's same-origin requests and settles only
when none has been open for 500ms, nothing is busy and the text is
unchanged. A page not settled by the cap is reported STILL LOADING
with a WAIT line naming the open requests, and EvaluatePage fails it.
The cap (a cap, not a sleep) is now 15s for run --page-check and the
playwright check --wait default; the run timeout scales with it. A page
with a list view, data grid or gallery always prints rows=, rows=0
included, so an empty list no longer reads like a page without one.

Verification: pagecheck_settle_test.go drives the real probe against a
fake page whose /xas/ answers late; with the old probe JS it fails with
"rows = 0, want 2" and "not reported as still loading", and the
verdict/EvaluatePage tests fail with the Go side reverted (the browser
tests skip without Node/Playwright). Runtime A/B on 11.13.0
(mdl-examples/bug-tests/pagecheck-845-slow-datasource.mdl): baseline
`run --local --page-check` gave slow2/slow8 "text=37" with no rows and
`playwright check` "OK 4 page(s)"; fixed gives rows=2 everywhere, and
`--wait 3s` on the 8s page gives STILL LOADING and exit 1 where the
baseline exited 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A view using STRING_AGG(expr, 'sep') next to GROUP BY was reported as
MDL036 "not aggregated and is not a GROUP BY expression", and check, exec
and exec --no-check all refused it (upstream mendixlabs#1641).

Cause: the GROUP BY rules (MDL034/035/036) and MDL037 recognised an
aggregate with a hand-written count|sum|avg|min|max regex. mxbuild's OQL
grammar has six aggregate tokens ('SUM' 'COUNT' 'AVG' 'MIN' 'MAX'
'STRING_AGG', DeprecatedOqlLexer in Mendix.Modeler.Core.dll). --no-check did
not help because execCreateViewEntity runs ValidateOQLSyntax itself as a
write barrier, and MDL036 is one of its rules. That barrier stays: MDL036
also fires on shapes mxbuild rejects (`length(s.Name)` next to
`group by b.Code` is CE0174 on 11.13.0), so --no-check must not write it.

Fix:
- oqlAggregateFunctions is the one list both regexes are built from, now
  with string_agg. A STRING_AGG over a grouped column is MDL034, as mxbuild
  reports (CE0174 "cannot both be aggregated and appear in the GROUP BY").
- Version gate oql_functions.string_agg: 11.2.0, and 10.24.22 on the 10.24
  line (max_version 10.99.99). Below it every such view is CE0174 "Unknown
  function 'string_agg'". Found by reading the lexer tokens out of each
  mxbuild's Mendix.Modeler.Core.dll and bisecting CDN tarballs, then
  confirmed with mx check at both ends of each boundary.
- The registry's display paths (show features, added since, the gate's
  "requires" text) took the lowest min_version, which for a two-line feature
  reported STRING_AGG unavailable on every 11.x and told an 11.1 project it
  needed 10.24.22. entryAt picks the entry covering the version, else the
  next floor above it.
- The STRING_AGG column type changed in 11.7.0: the argument's own type on
  11.2-11.6.12 (String(100) over a String(100), exactly), a derived
  String(200) from 11.7.0 and on 10.24.22+. It is judged where the project
  version is known (check -p / exec's reference pass) and left unjudged
  without one; on 11.2-11.6 only when the aggregated attribute already
  exists. The issue's own `Names: String` is CE6770 on every version, and
  check -p now says so with the fix for the project.

Verification:
- Unit tests for the grouping rules, the exec handler, the version gate, the
  type rule per era and the registry; with the production change reverted
  they fail with the reported MDL036 / "invalid OQL in view entity" refusal.
- mx check, probe views over String(100)/String(500)/unlimited sources:
  10.24.21, 11.0.0, 11.1.0 CE0174 "Unknown function 'string_agg'";
  10.24.22, 10.24.24, 11.7.0, 11.8.0, 11.9.1, 11.10.0, 11.12.2, 11.13.0,
  11.15.0 String(200); 11.2.0, 11.6.6, 11.6.8, 11.6.12 the argument's type.
- mdl-examples/bug-tests/view-entity-1650-string-agg.mdl: check -p passes,
  exec creates both views, mx check 0 errors and describe -> exec reports
  Unchanged on 10.24.22, 11.7.0, 11.12.2, 11.13.0, 11.15.0; its String(100)
  variant likewise on 11.2.0 and 11.6.6. On 11.1.0 and 10.24.21, check -p,
  exec and exec --no-check refuse it naming 11.2.0+ / 10.24.22+. The
  issue's step2 (`Names: String`) on 11.12.2: check -p and exec refuse with
  "Fix: change to 'Names: String(200)'".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A doc comment on its own is not an MDL statement, so the conformance gate
counted the new ```mdl block as a syntax finding (8 against an allowlist of
7). The gate's own guidance is to fence a non-MDL fragment as ```text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…operation declares (#1399)

Two items of the issue (mendixlabs#1517) were still open on main,
both because an alter block's variable verdicts — and those of the patch
`create or modify` splices with, which is one — were taken against the
flow as stored rather than the flow the block writes:

- Repro B (variable scope in one alter block): the fragment builder was
  seeded only with the stored flow's variables, so after
  `insert after $X begin declare $Late Integer = 0; end;` a replaced loop
  doing `set $Late = $Late + 1` failed at exec with "variable 'Late' is
  not declared", while check gave up on it ("not predicted") and passed.
  2f42df6 had fixed the scope check for a fragment that only READS such
  a variable; building one that sets it, or names a member of an entity
  an inserted retrieve returns, still failed.
- Used return: a producer moved upstream — `create or modify` restating
  `$Code = call …` above the declare it followed, which the diff turns
  into insert + drop — was refused "the fragment declares $Code, which
  the microflow already has" by the stored producer the same patch drops,
  and in the other order "$Code is still used by: log …; return $Code"
  by readers the inserted producer now feeds.

Fix (mdl/executor/cmd_alter_flow.go):
- each applied fragment's declared variables, with the types its builder
  resolved, seed the builders of later fragments (fragmentBuilder,
  buildFragmentVars, noteFragment); check's pre-build in
  alterFlowViolations does the same, so the two agree. In scope is still
  checkFragmentScope's to say, so a declaration downstream of the user is
  refused as before;
- the clash with a stored producer is judged once every operation is
  applied (checkDeclaredUnclashed), skipping one the block takes away or
  whose loop it replaces;
- checkOutputUnused no longer counts a reader — stored, or in a fragment
  — that another operation's fragment declares the variable for upstream
  of it (declaredOnPath), nor one inside a replaced loop.

A body that builds declares each variable once, so a create-or-modify
clash refusal from the splice was always false: the #1361 test that
pinned "replace $Equal: the fragment declares $Different, which the
microflow already has" as the expected verdict now asserts that check
predicts the splice, exec writes the declared flow, and the start event,
the log and the end event keep their $IDs.

Kept as is: a declaration made by a LATER operation of the block is not
known to an earlier fragment (check: "not predicted"; exec refuses) —
the skill now says "declare first, use after".

Verification:
- New tests (cmd_alter_flow_block_vars_1399_pedapp_test.go): Repro B for
  a primitive and an entity variable, with check required to PREDICT the
  statement (no "not predicted" note); a producer moved earlier through
  create or modify (only the producer re-created: 1 gone, 1 new $ID) and
  through alter in both orders and with its reader; controls (declared
  after the loop; producer moved past its reader; stored producer kept;
  a reader ahead of the new producer) still refused by check and exec.
- Revert proof: with cmd_alter_flow.go and validate_flow_verdicts.go
  restored, all new tests and the updated #1361 test fail with the
  reported messages.
- Blank 11.13.0 app, mdl-examples/bug-tests/splice-1399-…mdl: baseline
  stops at the alter ("variable 'Late' is not declared") and rolls back;
  without the alter it stops at "the fragment declares $Code". Fixed:
  "Altered microflow BugTest1399.LoopFlow", "Modified microflow:
  BugTest1399.MoveFlow (spliced: 1 inserted, 1 dropped)"; re-runnable;
  mx check 0 errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cess, and CE0001 associations are written (#819)

Three follow-ups from #802 (PR #809); two were still open on main.

1. `create or modify association` moving a cross-module OWNER Both
   association's TO end to another module (or entity) printed "Modified
   association" and mx check reported CE0066 "Entity access is out of date"
   at the old TO module and the new one. Changing only the owner through
   create or modify missed the TO module the same way.
   Cause: the cross-module OR MODIFY branch of execCreateAssociation
   (mdl/executor/cmd_associations.go) overwrote ChildRef/owner and reconciled
   nothing; the end-of-program reconcile covers the declaring module only.
   And ReconcileMemberAccesses kept the old TO entity's entry even when its
   module was reconciled, because a reference to another module's existing
   association was preserved on faith (#758's rule for inheritance across
   modules).
   Fix: the branch reads the old TO module before overwriting and reconciles
   declaring, old TO and new TO modules (reconcileModulesAccess). The
   reconcile treats an existing foreign association as stale on an entity
   whose generalization chain never leaves its module — its members are then
   fully known (own + incoming OWNER Both). Inheritance across modules stays
   preserved (control test).

2. Between a persistent and a non-persistent entity, Mendix accepts only
   FROM the non-persistent end with owner Default; `owner both`, a persistent
   FROM end, and `alter association … set owner both` all passed check and
   exec and failed CE0001. Now refused as MDL-ASSOC01 in `check`
   (ValidateProgram, script declarations first, then the project) and in
   `exec` (create, create or modify, alter set owner both — also under
   --no-check), through one predicate (associationPersistenceViolation) and
   one stored-ends lookup (storedAssociationEnds).

3. GRANT accepting the TO-side member of a cross-module OWNER Both
   association already works on main (74bc820): `grant read (Number,
   Lead_Order) on entity Shop.Order …` writes the member, mx check 0 errors.
   No change.

Verified on a blank 11.13.0 app with security on, mx check before (baseline
binary) / after: TO end moved to another module 2 -> 0 (CE0066 at both TO
modules), within the module 1 -> 0, owner Default->Both via create or modify
1 -> 0; `update security` repairs the move broken by the baseline binary.
CE0001 matrix: NP->P both, P->NP default, P->NP both, cross-module NP->P both,
ReferenceSet NP->P both and alter set owner both were each 1 CE0001 before and
are refused now with the project untouched (0 errors); NP->P default is still
written (0 errors). No skill or mdl-examples script trips the new check.
Tests revert-proven (fix reverted, predicate stubbed): every new test fails,
the controls pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…935)

`create java action M.JA (C: M.Color) returns Boolean`, with M.Color an
enumeration, passed check and exec and was written as an entity parameter;
mxbuild reported CE1613 "The selected entity 'M.Color' no longer exists".
The same held for the return type, for JavaScript actions, for a System
enumeration, and for a name that is neither an entity nor an enumeration.

Cause: a bare `Module.Name` in a code-action signature parses as
TypeEnumeration without ExplicitEnum (the TypeEnumeration/TypeEntity
ambiguity), and astDataTypeToJavaActionParamType / ...ReturnType made every
such name a CodeActions$EntityType. Their comment said callers resolve it
"via resolveCodeActionParamType", a function that never existed.

Fix: settleCodeActionSignature decides each bare name against the project
before anything is written - an entity stays an entity, an enumeration
(project, System, or created earlier in the script) becomes an explicit
enumeration, and a name that is neither is refused. An explicit `enum` is
held to enumerations. check --references reports the same verdict through
codeActionSignatureErrors (declaredTypeResolver). The stale comment now
points at the real function.

Verification: tests in cmd_javaactions_bare_enum_935_test.go (Java and
JavaScript, parameter and return, refusal, check side) fail with the fix
reverted and pass with it. Blank 11.13.0 app with
mdl-examples/bug-tests/java-action-935-bare-enumeration-type.mdl: mx check
CE1613 x5 with the pre-fix binary, 0 errors fixed; describe -> exec reports
"already in sync". Flow parameters already resolved the ambiguity (control
microflow in the script). go vet and go test ./mdl/executor/ pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`create java action M.J (Subject: StringTemplate(Text))` - the form describe
prints for a text-template parameter - wrote a String parameter, so a
describe -> exec of, say, BusinessEvents.PublishBusinessEvent_V2 silently
turned its template into a String and every call Studio Pro had written for
it stopped matching (CE0115). Found while fixing #1451, whose
check-side template validation reads the declared type from the script.

Cause: astDataTypeToJavaActionParamType had no case for ast.TypeStringTemplate
and returned its default StringType. The Java source generator mapped the
type to java.lang.String, pinned by a golden (JA_Tpl) that had been measured
on an action this bug created with String parameters.

Fix: StringTemplate(Text|Sql) converts to a StringTemplateParameterType with
that grammar, and the generated .java declares it as
com.mendix.systemwideinterfaces.javaactions.parameters.IStringTemplate, fully
qualified - what mxbuild generates (BusinessEvents 3.12's source).

Verification: TestParamType_StringTemplateIsATemplate and the JA_Tpl golden
fail with the fix reverted. On a blank 11.13.0 app, mxcli's .java for a
Sql + Text template action is byte-identical to the one an mxbuild
--target=deploy build leaves, and a copy edited back to java.lang.String is
rewritten to IStringTemplate by the build. describe prints
StringTemplate(Sql)/StringTemplate(Text) for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… an expression (#1451)

Mirror of mendixlabs#1554. A Java action parameter of type
StringTemplate(Text|Sql) - BusinessEvents.PublishBusinessEvent_V2's Subject -
or a custom document - AgentEditorCommons' Agent - was bound as a
Microflows$BasicCodeActionParameterValue holding the argument's source text.
check --references and exec passed; mx check reported CE0115 at the call.
describe java action showed the document parameter as `String`.

Cause: addCallJavaActionAction gave only entity-type and Microflow parameters
their own value; the two others fell to the expression default. The document
parameter type read back as a String (codeActionParamTypeFromGen had no case),
and MDL had no way to write a template's {n} arguments.

Fix:
- A text-template argument is written the way a log message is,
  `Subject = 'Order {1}' with ({1} = $Order/Number)` (grammar: templateParams
  on a call argument, refused outside call java action). It stores a
  Microflows$StringTemplateParameterValue: the text plus one
  Microflows$PrimitiveTypedTemplateArgument per placeholder, each typed from
  its expression (String/Integer/Decimal/Boolean/DateTime; an enumeration is
  refused). A plain string is the text; another expression is '{1}' with it
  as the argument (the #1444 rule). An expression followed by
  `with (...)` is refused (MDL-JA02, no project needed).
- A document parameter reads as CustomBlobDocumentType (describe:
  `Document(agenteditor.agent)`; written back unchanged on a rewrite) and its
  argument - `Agent = Module.Doc` or 'Module.Doc' - stores a
  Microflows$CustomBlobDocumentCodeActionParameterValue. check --references
  resolves agent-editor documents (missing: CE1613, wrong type: CE0115).
- describe prints both re-executably; '{1}' with one non-literal argument
  prints as that expression, so describe -> exec reports Unchanged.

No Studio Pro-authored example of either value exists in the repo's fixtures
or on this machine. The shapes are the metamodel's, measured against mxbuild
11.13 by patching units: accepted as written; CE0117 when an argument's Type
is not its expression's (absent Type = String; an enumeration takes none);
CE0720 for a placeholder without an argument.

Verification: the new tests fail with the builder, reader, writer, describe
and reference-check changes stubbed out. mx check on 11.13.0:
BusinessEvents 3.12 app, PublishBusinessEvent_V2 calls - pre-fix CE0115 x2,
fixed 0 errors (plain, expression, and `with` with an Integer and
[%CurrentDateTime%]); bug-test script on a blank app - 0 errors, control
(fixed action, pre-fix microflow) CE0115 x2; synthetic document-parameter
action - pre-fix shape CE0115, fixed 0 errors. describe -> exec: Unchanged
microflow in all three. go vet and go test pass for the touched packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A text template (StringTemplate(Text|Sql)) is a Java action type only, but
the grammar reads it for JavaScript actions too and both create handlers
share one type converter. On main, `create javascript action M.JSA
(S: StringTemplate(Text))` was silently written as a String parameter
(describe: `S: String`); after the Java-action StringTemplate fix on this
branch it was written as a CodeActions$StringTemplateParameterType into the
JavaScript action. mx check 11.13.0 reports 0 errors for both, so nothing
told the author. A StringTemplate return type was written as Boolean.

Fix: unsupportedJavaScriptActionTypes is the one decision. exec refuses the
statement with it before anything is written, and check reports it as
MDL-JSA01 in the no-project tier (validate_program.go), so exec's pre-flight
refuses it too. Parameters and the return type are both covered.
Document(...) has no MDL spelling, so the parser already refuses it for
either kind of action.

Verification: TestCreateJavaScriptAction_StringTemplateIsRefusedAtExec and
TestCreateJavaScriptAction_StringTemplateIsRefusedAtCheck fail against a
stubbed validator (patch revert) and pass with the fix. Blank 11.13.0 app:
main and the pre-refusal branch both exec the script at mx check 0 errors
(main as String parameters); now check, exec and exec --no-check all
refuse it and nothing is written. Fixture
mdl-examples/bug-tests/javascript-action-1451-string-template.fail.mdl.
go vet and go test ./mdl/executor/ ./mdl/conformance/ pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…A03)

`create java action M.J () returns StringTemplate(Text)` passed check and
exec and was written as a Boolean action; so were `returns Binary`,
`returns AutoNumber` and `returns entity <T>`, for Java and JavaScript
actions alike. Found while fixing #1451.

Cause: astDataTypeToJavaActionReturnType ended in a default that turned
every kind it did not handle into Boolean. The deliberate default (no
`returns` clause: Boolean, Studio Pro's default for a new action) and the
fall-through shared one branch.

Measured: a Java action cannot return a text template. generated/metamodel
types JavaAction.ActionReturnType (storage JavaReturnType) as
CodeActions$Type, whose implementers do not include
StringTemplateParameterType; a unit patched to carry one makes mx check
11.13.0 fail to load the project ("Type
Mendix.Modeler.CodeActions.StringTemplateParameterType does not contain a
constructor with a parameter of type Mendix.Modeler.JavaActions.JavaAction").
main's Boolean form checks at 0 errors - the declared type was simply lost.

Fix: the converter returns an error for any kind it does not handle; only
an omitted `returns` is Boolean. Both create handlers refuse with it before
anything is written. check reports the same verdict as MDL-JA03 by calling
the converter through javaActionReturnTypeError (type parameters resolved
first, as exec does), so the two cannot disagree; for a JavaScript action a
StringTemplate return stays reported once, as MDL-JSA01.

Verification: TestCreateJavaAction_UnreturnableTypeIsRefusedAtExec and
TestValidate_UnreturnableTypeIsReportedAtCheck fail against the reverted
fix (patch revert) and pass with it; the control
TestCreateJavaAction_OmittedReturnTypeIsBoolean passes both ways. On an
11.13.0 app, check and exec --no-check refuse the statement and nothing is
written. Fixture java-action-1451-return-string-template.fail.mdl.
go vet and go test ./mdl/executor/ ./cmd/mxcli/syntax/ ./mdl/conformance/
pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: mxcli test passes what it did not check — multi-line @expect (#1514) and page-check before data (#845)
fix: STRING_AGG in a view entity is refused as not aggregated (#1650)
…cess

fix: access rules out of step after drop entity and association moves (#1449, #819)
fix: microflow splice refusals — find/filter where respelling (#856), alter-block variables (#1399)
fix: Java/JavaScript action signatures and template/document arguments written wrong (#1451, #935)
@ako
ako merged commit 9eeb6e0 into mendixlabs:main Oct 25, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

drop entity leaves the association member in the other entity's access rule (mx check CE1613); drop association already reconciles

1 participant